package com.luck.cloud.configuration;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;


/**
 * @EnableScheduling :开启定时任务
 * @ComponentScan("com.luck.cloud") :扫描这个路径
 * @ConditionalOnClass:当存在这个类的时候才加载,判断是否引入了redis
 */

@Slf4j
@Configuration
@EnableScheduling
@ComponentScan("com.luck.cloud")
@ConditionalOnClass(RedisTemplate.class)
public class SnowflakeAutoConfiguration {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 固定前缀
     */
    public final static String SNOWFLAKE_MACHINE_ID_PREFIX = "SNOWFLAKE_MACHINE_ID_PREFIX:";

    /**
     * 数据中心ID
     */
    public static long DATACENTER_ID = 0L;

    /**
     * 机器ID
     */
    public static long WORKER_ID = 0L;


    /**
     * 当存在redis的配置时候才生效
     *
     * @return
     */

    @Bean
    @ConditionalOnProperty(name = "spring.redis.host")
    public Snowflake snowflake() {
        //加载有效信息
        loadAvailableInfo();
        //注册bean
        return IdUtil.getSnowflake(WORKER_ID, DATACENTER_ID);
    }


    /**
     * 加载有效的数据中心Id和机器Id
     * 原理是redis锁
     * 设置redis的key有效期为10分钟,十分钟后如果不续期就会自动释放这个key,避免被长期占用,最终导致key被耗尽.
     * 因为数据中心id和机器id的有效值是0-31,但是为了排除0所以有效值就成了 1-31 共31个
     * 数据中心id:[1-31]
     * 机器id:[1-31]
     * 总共就可以支持31*31 =961个服务,超过的就会使用 DATACENTER_ID=0,WORKER_ID=0这个默认的参数
     */
    private void loadAvailableInfo() {
        for (int dataCenterId = 1; dataCenterId < 32; dataCenterId++) {
            for (int workId = 1; workId < 32; workId++) {
                String key = SNOWFLAKE_MACHINE_ID_PREFIX + dataCenterId + ":" + workId;
                Boolean hasKey = stringRedisTemplate.hasKey(key);
                if (hasKey == null || !hasKey) {
                    log.info("    雪花算法生成因子key:{}可以使用", key);
                    //该key可以使用
                    DATACENTER_ID = dataCenterId;
                    WORKER_ID = workId;
                    //设置一个数据,1小时有效
                    stringRedisTemplate.opsForValue().set(key, key, 10, TimeUnit.MINUTES);
                    return;
                }
            }
        }
        log.info("    为找到可用的数据中心id和机器id,使用默认的key:SNOWFLAKE_MACHINE_ID_PREFIX:0:0");
    }

}
